/*
	This is a part of the source code for Pro/DESKTOP.
	Copyright (C) 1998-1999 Parametric Technology Corporation.
	All rights reserved.
*/


// Designing a Hexagonal Bolt

#include <StdAfx.h>
#include <math.h>

//Interface includes
#include "IApplication.h"
#include "IPartDocument.h"
#include "IGraphicDocument.h"
#include "IaWorkplane.h"
#include "IaSketch.h"
#include "IaLine.h"
#include "IzStraight.h"
#include "ISet.h"
#include "IzVector.h"
#include "IzPlane.h"
#include "IaGeometric.h"
#include "IaExtrusion.h"
#include "IHelm.h"

//Global Variables
IApplication *pdApp;

//==============================================================================================================
//Forward declarations of some convenience functions used by CreateHexagonalBolt()
HRESULT GetProDESKTOPApplication(IApplication **proDApp);
HRESULT GetActivePartWorkplaneSketch(IPartDocument **activePart, IGraphicDocument **activeGraphicDoc, IWorkplane **activeWorkplane, ISketch **activeSketch);
HRESULT CommitToProDESKTOP(CString str,BOOL bl = FALSE);
HRESULT NewSketch(IWorkplane *workplane, int color, CString sketchName, ISketch **pSketch);
HRESULT CreateLine(double startX, double startY, double endX, double endY, ILine **dLine1);
HRESULT CreateCircle(double centerX, double centerY, double radius, ILine **dCircle1);
HRESULT CreateExtrusion(ISketch *pSketch, double distanceAbove, int distanceBelow, double taperAngle, int side, long materialStatus, CString extrusionName, IExtrusion **pExtrusion);

//macro defintions
#define CHECK_RETURN_STATUS(status) \
	if (FAILED(status))
		return E_FAIL;

#define CAST(cls, obj) \
	QueryInterface<cls>(obj,IID_##cls) \

template <class T> 
T *QueryInterface(IDispatch *disp,GUID iid)
{
	void *pUnk;
	HRESULT status = disp->QueryInterface(iid,(void **)&pUnk);
	if (FAILED(status)) { 
		ASSERT(FALSE);
		AfxThrowOleException(status);
	} 
	disp->Release();
	return (T *)pUnk;

}

#define GetCLASS(cls) \
	GetFormalClass<I##cls##Class>(IID_I##cls##Class, #cls) \

template <class T>
T *GetFormalClass(GUID iid, CString clsName)
{
	IDispatch *disp = NULL;
	HRESULT status = pdApp->GetClass(clsName.AllocSysString(), &disp);

	T *thisCls = NULL;
	status = disp->QueryInterface(iid, (void **)&thisCls);
	if(status || !thisCls) {
		AfxMessageBox("QueryInterface Failed");
		return NULL;
	}
	return thisCls;
}

//==============================================================================================================

HRESULT CreateHexagonalBolt(double length, double dia)
{
	HRESULT status = NOERROR;
	CString str1, str2, str3, ext1, ext2, ext3;

	double outerCircDia = (dia*sqrt(3.))/2;
	int sketchCount = 0;
	int operationCount = 0;

	pdApp = NULL;
	IApplication *pdApplication = NULL;
	status = GetProDESKTOPApplication(&pdApplication);
	CHECK_RETURN_STATUS(status)
	pdApp = pdApplication;

	IPartDocument *newPart = NULL;
	status = pdApplication->NewPart(&newPart);
	CHECK_RETURN_STATUS(status)

	IGraphicDocument *activeGraphicDoc = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;

	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	IExtrusion *pExtrusion = NULL;
	ISketch *pSketch = NULL;

	str1.Format("sketch %d", ++sketchCount);
	status = NewSketch(activeWorkplane, 6, str1.AllocSysString(), &pSketch);
	CHECK_RETURN_STATUS(status)

	ILine *pLine = NULL;
	status = CreateLine(dia, 0, dia/2, outerCircDia, &pLine);
	CHECK_RETURN_STATUS(status)

	status = CreateLine(dia/2, outerCircDia, -dia/2, outerCircDia, &pLine);
	CHECK_RETURN_STATUS(status)

	status = CreateLine(-dia/2, outerCircDia,  -dia, 0, &pLine);
	CHECK_RETURN_STATUS(status)

	status = CreateLine(-dia, 0, -dia/2, -outerCircDia, &pLine);
	CHECK_RETURN_STATUS(status)

	status = CreateLine(-dia/2, -outerCircDia, dia/2, -outerCircDia, &pLine);
	CHECK_RETURN_STATUS(status)

	status = CreateLine(dia/2, -outerCircDia, dia, 0, &pLine);
	CHECK_RETURN_STATUS(status)

	ext1.Format("operation %d", ++operationCount);
	status = CreateExtrusion(pSketch, 0.8*dia, 1, 0, 0, 1, ext1.AllocSysString(), &pExtrusion);
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("Extrusion1");
	CHECK_RETURN_STATUS(status)

	status = activePart->UpdateDesign();
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("Updatedesign1");
	CHECK_RETURN_STATUS(status)

	//draw circle in another sketch
	str2.Format("sketch %d", ++sketchCount);

	status = NewSketch(activeWorkplane, 6, str2.AllocSysString(), &pSketch);
	CHECK_RETURN_STATUS(status)

	ILine *pCircle = NULL;
	status = CreateCircle(0, 0, outerCircDia, &pCircle);
	CHECK_RETURN_STATUS(status)

	ext2.Format("operation %d", ++operationCount);
	pExtrusion = NULL;
	status = CreateExtrusion(pSketch, .8*dia, 1, -59*3.142/180, 0, 3, ext2.AllocSysString(), &pExtrusion);
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("Extrusion2");
	CHECK_RETURN_STATUS(status)

	status = activePart->UpdateDesign();
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("UpdateDesign2");
	CHECK_RETURN_STATUS(status)

//  draw circle in another sketch
	str3.Format("sketch %d", ++sketchCount);

	status = NewSketch(activeWorkplane, 6, str3.AllocSysString(), &pSketch);
	CHECK_RETURN_STATUS(status)

	status = CreateCircle(0, 0, dia/2, &pCircle);
	CHECK_RETURN_STATUS(status)

	ext1.Format("operation %d", ++operationCount);
	pExtrusion = NULL;
	status = CreateExtrusion(pSketch, length+0.8*dia, 1, 0, 0, 1, ext3.AllocSysString(), &pDisp);
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("Extrusion3");
	CHECK_RETURN_STATUS(status)

	status = activePart->UpdateDesign();
	CHECK_RETURN_STATUS(status)

	status = CommitToProDESKTOP("UpdateDesign3");
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

//===========================================================================================================

HRESULT GetProDESKTOPApplication(IApplication **proDApp)
{
	HRESULT status = NOERROR;
	IApplication *app = NULL;

	if (pdApp = NULL) {
		status = CoCreateInstance(CLSID_ProDESKTOP, NULL, CLSCTX_SERVER, IID_IApplication, (void**)&app);
		CHECK_RETURN_STATUS(status)

		status = (app)->SetVisible(TRUE);
		CHECK_RETURN_STATUS(status)
		*proDApp = app;
	}
	else 
		proDApp = pdApp;

	return NOERROR;
}

HRESULT GetActivePartWorkplaneSketch(IPartDocument **activePart,
									 IGraphicDocument **activeGraphicDoc,
									 IWorkplane **activeWorkplane,
									 ISketch **activeSketch)
{
	HRESULT status = NOERROR;

	IPartDocument *part = NULL;
	IWorkplane *workplane = NULL;

	ISketch * sketch = NULL;
	IApplication *app = NULL;

	// Get the application
	status = GetProDESKTOPApplication(&app) ;
	CHECK_RETURN_STATUS(status)

	status = app->GetActiveDoc(activeGraphicDoc);
	CHECK_RETURN_STATUS(status)

	*activePart = CAST(IPartDocument, *activeGraphicDoc);

	// Get active workplane
	status = (*activeGraphicDoc)->GetActiveWorkplane(activeWorkplane);
	CHECK_RETURN_STATUS(status)

	// Get active sketch
	status = (*activeGraphicDoc)->GetActiveSketch(activeSketch);
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

HRESULT CommitToProDESKTOP(CString str,BOOL bl)
{
	HRESULT status = CONV_SUCCESS;

	// Take the helm
	IHelm *pHelm = NULL;
	status = GetProDESKTOPApplication(&app);
	CHECK_RETURN_STATUS(status)

	status = app->TakeHelm(&pHelm);
	CHECK_RETURN_STATUS(status)

	// Committing the operation
	status = pHelm->CommitCalls(str.AllocSysString(), bl);
	CHECK_RETURN_STATUS(status)

	pHelm->Release();

	return NOERROR;
}

HRESULT NewSketch(IWorkplane *workplane, int color, CString sketchName, ISketch **pSketch)
{
	HRESULT status = CONV_SUCCESS;

	IGraphicDocument *activeGraphicDoc = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;

	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	status = workplane->CreateSketch(sketchName.AllocSysString(), &sketch);
	CHECK_RETURN_STATUS(status)

	// Set the color for the sketch
	IColor *pNewColor= NULL;
	status = GetCLASS("Color")->CreateColor(1, color * 30, 0.35, 1,&pNewColor);
	CHECK_RETURN_STATUS(status)

	status = sketch->SetColor(pNewColor);
	CHECK_RETURN_STATUS(status)

	status = activeGraphicDoc->SetActiveSketch(sketch);
	CHECK_RETURN_STATUS(status)
	*pSketch = sketch;

	return NOERROR;
}

HRESULT CreateLine(double startX, double startY, double endX, double endY, ILine **dLine1)
{
	HRESULT status = CONV_SUCCESS;

	IGraphicDocument *activeGraphicDoc = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;

	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	// Create the startVector
	IVector *startVector2D = NULL;
	status = GetCLASS("Vector")->CreateVector(startX, startY, 0, &startVector2D);
	CHECK_RETURN_STATUS(status)

	// Get the 3D vector for startVector
	IVector *startVector3D = NULL;
	status = activeWorkplane->Get3DVector(startVector2D, &startVector3D);
	CHECK_RETURN_STATUS(status)

	// Create the endVector
	IVector *endVector2D = NULL;
	status = GetCLASS("Vector")->CreateVector(endX, endY, 0, &endVector2D);
	CHECK_RETURN_STATUS(status)

	// Get the 3D vector for endVector
	IVector *endVector3D = NULL;
	status = activeWorkplane->Get3DVector(endVector2D, &endVector3D);
	CHECK_RETURN_STATUS(status)

	// Create basic straight line
	IBasicStraight *straight1 = NULL;
	status = GetCLASS("BasicStraight")->CreateBasicStraightTwoPoints(startVector3D, endVector3D, &straight1);
	CHECK_RETURN_STATUS(status)

	// Create aLine object
	ILine *line1 = NULL;
	status = activeSketch->CreateLine(CAST(ICurve, straight1), &line1);
	CHECK_RETURN_STATUS(status)
	*dLine1 = line1;

	// Apply AutoConstrain on the line created
	status = activeWorkplane->AutoConstrain(CAST(IObjectOrSet, line1));
	CHECK_RETURN_STATUS(status)

	// Set the selection to the line created
	status = activeGraphicDoc->SetSelection(CAST(IObjectOrSet, line1));
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

HRESULT CreateCircle(double centerX, double centerY, double radius, ILine **dCircle1)
{
	HRESULT status = CONV_SUCCESS;

	IGraphicDocument *activeGraphicDoc = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;

	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	// Get geometric interface
	IGeometric *geometric = CAST(IGeometric, activeWorkplane);

	// Get zPlane
	IGeometry *pGeometry = NULL;
	status = geometric->GetGeometry(&pGeometry);
	CHECK_RETURN_STATUS(status)

	IPlane *plane = CAST(IPlane, pGeometry);

	// Create centerVector
	IVector *centerVector2D = NULL;
	status = GetCLASS("Vector")->CreateVector(centerX, centerY, 0, &centerVector2D);
	CHECK_RETURN_STATUS(status)

	// Get the 3D vector for centerVector
	IVector *centerVector3D = NULL;
	status = activeWorkplane->Get3DVector(centerVector2D, &centerVector3D);
	CHECK_RETURN_STATUS(status)

	// Get normal to the plane
	IDirection *normal = NULL;
	status = plane->GetNormal(&normal);
	CHECK_RETURN_STATUS(status)

	// Create basic circle
	IBasicCircle *basicCircle1 = NULL;
	status = GetCLASS("BasicCircle")->CreateBasicCircle(centerVector3D, normal, radius, &basicCircle1);
	CHECK_RETURN_STATUS(status)

	// Create aLine object
	ILine *circle1 = NULL;
	status = activeSketch->CreateLine(CAST(ICurve, basicCircle1), &circle1);
	CHECK_RETURN_STATUS(status)
	*dCircle1 = circle1;

	// Apply AutoConstrain on the circle created
	status = activeWorkplane->AutoConstrain(CAST(IObjectOrSet, circle1));
	CHECK_RETURN_STATUS(status)

	// Set the selection to the circle created
	status = activeGraphicDoc->SetSelection(CAST(IObjectOrSet, circle1));
	CHECK_RETURN_STATUS(status)

	return NOERROR;
}

HRESULT CreateExtrusion(ISketch *pSketch, double distanceAbove, int distanceBelow, double taperAngle, int side, long materialStatus, CString extrusionName, IExtrusion **pExtrusion)
{
	HRESULT status = CONV_SUCCESS;

	IGraphicDocument *activeGraphicDoc = NULL;
	IPartDocument *activePart = NULL;
	IWorkplane *activeWorkplane = NULL;
	ISketch *activeSketch = NULL;
	
	status = GetActivePartWorkplaneSketch(&activePart, &activeGraphicDoc, &activeWorkplane, &activeSketch);
	CHECK_RETURN_STATUS(status)

	IDesign *pDesign = NULL;
	status = activePart->GetDesign(&pDesign);
	CHECK_RETURN_STATUS(status)

	IExtrusion *extrusion = NULL;
	status = GetCLASS("Extrusion")->CreateExtrusion(pDesign, pSketch, distanceAbove, distanceBelow, taperAngle, side, materialStatus, &extrusion);
	CHECK_RETURN_STATUS(status)

	IOperation *operation = CAST(IOperation, extrusion);
	status = operation->SetName(extrusionName.AllocSysString());
	CHECK_RETURN_STATUS(status)
	*pExtrusion = extrusion;

	return NOERROR;
}


